package jersey.czjug;

import com.sun.jersey.api.NotFoundException;
import com.sun.jersey.api.view.Viewable;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

/**
 * Web resource providing RESTful API to manage system properties.
 * The resource handles URL's starting with "/properties" prefix and
 * produces text/plain, text/xml, application/xml and application/json media
 * types.
 */
@Path("/properties")
@Produces({"text/plain", "text/xml", "application/xml"})
@Component
@Scope("singleton")
public class PropertyResource {
    /**
     * Method to check if there is a system property of a given name.
     * If not, it throws the NotFoundException which get's transfered into a response
     * with 404 status code (Not Found).
     *
     * @param propertyName Name of a property to check.
     */
    private static void checkPropertyExists(String propertyName) {
        if (!System.getProperties().containsKey(propertyName)) {
            throw new NotFoundException("Error - Property Not Found: " + propertyName);
        }
    }

    /**
     * Method to handle HTTP GET requests for URL's matching "/properties/{property}".
     * Returns a representation of a single system property identified by the name {property}.
     *
     * @param propertyName Name of the property to get (extracted from the request URL)
     * @return PropertyBean representing the property.
     * @throws com.sun.jersey.api.NotFoundException
     *          If there is no system property of a given name.
     */
    @GET
    @Path("/{property}")
    public PropertyBean getJAXBProperty(@PathParam("property") String propertyName) {
        checkPropertyExists(propertyName);
        return new PropertyBean(propertyName, System.getProperty(propertyName));
    }

    /**
     * Method to handle HTTP GET requests for URL's matching "/properties/{property}"
     * producing text/html media type.
     *
     * @param propertyName Name of the property to get (extracted from the request URL)
     * @return Viewable that is used to render the property using a JSP template
     * @throws com.sun.jersey.api.NotFoundException
     *          If there is no system property of a given name.
     */
    @GET
    @Path("/{property}")
    @Produces("text/html")
    public Viewable getHTMLProperty(@PathParam("property") String propertyName) {
        return new Viewable("/property", getJAXBProperty(propertyName));
    }

    /**
     * Method to handle HTTP GET requests for URL matching "/properties".
     * Returns a list of all system properties.
     *
     * @return List of PropertyBean objects representing individual system properties.
     */
    @GET
    public List<PropertyBean> getProperties() {
        List<PropertyBean> result = new ArrayList<PropertyBean>(System.getProperties().size());

        // put the property names into a TreeSet to sort them
        for (Object name : new TreeSet(System.getProperties().keySet())) {
            result.add(new PropertyBean((String) name, System.getProperty((String) name)));
        }

        return result;
    }

    /**
     * Method to handle HTTP GET requests for URL matching "/properties" producing
     * text/html media type.
     *
     * @return list of all system properties
     */
    @GET
    @Produces("text/html")
    public Viewable getHTMLProperties() {
        return new Viewable("/properties", getProperties());
    }

    /**
     * Method handling HTTP PUT requests for URL's matching "/properties/{property}".
     * Adds a new system property, or updates an existing property identified by
     * the name {property}. Depending on whether a new system property was created
     * or an existing property was updated with a new value the response either contains
     * 201 status code (meaning Created) or 200 status code (meaning OK).
     *
     * @param uriInfo Object describing the request URI - injected by the Jersey framework.
     * @param name    Name of the property to add/update - extracted from the request URL.
     * @param value   New value for the property - extracted from the request content.
     * @return Reponse object containing a PropertyBean object representing the new
     *         system property as the content, and the right status code based on whether a new
     *         property was created or an existing one got updated.
     */
    @PUT
    @Path("/{property}")
    public Response setProperty(@Context UriInfo uriInfo,
                                @PathParam("property") String name, String value) {
        boolean isNew = !System.getProperties().containsKey(name);

        System.setProperty(name, value);
        PropertyBean result = new PropertyBean(name, value);
        if (isNew) {
            return Response.created(uriInfo.getAbsolutePath()).entity(result).build();
        } else {
            return Response.ok(result).build();
        }
    }

    /**
     * Method handling HTTP DELETE requests for URL's matching "/properties/{property}.
     * Deletes the property identified by the name {property}, or thorws an exception
     * if such property does not exist.
     *
     * @param propertyName Name of the property to delete.
     * @throws com.sun.jersey.api.NotFoundException
     *          If the property of a given name does not exist.
     */
    @DELETE
    @Path("/{property}")
    public void deleteProperty(@PathParam("property") String propertyName) {
        checkPropertyExists(propertyName);
        System.getProperties().remove(propertyName);
    }
}
